#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Ink;
using UTable.Objects.Controls;
using System.Windows;
using UTable.Objects.Handlers;
using UTable.Input.MultiTouch;
using UTable.Input;
using System.Windows.Shapes;

namespace UTable.Objects.Controls
{
	[TemplatePart(Name = "PART_InkCanvas", Type = typeof(InkCanvas))]
	public class UInkCanvas : UControl
	{
		static UInkCanvas()
		{
			DefaultStyleKeyProperty.OverrideMetadata(typeof(UInkCanvas), new FrameworkPropertyMetadata(typeof(UInkCanvas)));
		}

		public UInkCanvas()
		{
            this.InputReceived += new InputEventHandler(UInkCanvas_InputReceived);
		}

        void UInkCanvas_InputReceived(InputEventArgs args)
        {
			if (args is MultiTouchEventArgs && IsWriteEnable)
            {
                handler_MultiTouch(args as MultiTouchEventArgs);
                args.Handled = true;
            }
        }

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();
			WritingCanvas = this.GetTemplateChild("PART_InkCanvas") as InkCanvas;
		}

        public void AddNewStroke(List<Point> line)
        {
            System.Windows.Input.StylusPointCollection points = new System.Windows.Input.StylusPointCollection();
            foreach (Point p in line)
                points.Add(new System.Windows.Input.StylusPoint(p.X, p.Y));
            Stroke stroke = new Stroke(points);
            stroke.DrawingAttributes.FitToCurve = true;
            stroke.DrawingAttributes.IsHighlighter = true;
            stroke.DrawingAttributes.Color = WrittenStrokeColor;
            stroke.DrawingAttributes.Width = WrittenStrokeThickness;
            this.WritingCanvas.Strokes.Add(stroke);
        }

        public void ClearStrokes()
        {
            this.WritingCanvas.Strokes.Clear();
        }

        #region Properties

        private int writtenStrokeThickness = 4;

        private Color writtenStrokeColor = Colors.Black;

        public bool OnlyNotifyLineEnd = true;

        public InkCanvas WritingCanvas;

        public int WritingStrokeThickness = 6;

        public Color WritingStrokeColor = Colors.Black;

        public int WrittenStrokeThickness
        {
            get
            {
                return writtenStrokeThickness;
            }
            set
            {
                writtenStrokeThickness = value;
            }
        }

        public Color WrittenStrokeColor
        {
            get
            {
                return writtenStrokeColor;
            }
            set
            {
                writtenStrokeColor = value;
            }
        }

		public bool IsWriteEnable
		{
			get { return (bool)GetValue(IsWriteEnableProperty); }
			set { SetValue(IsWriteEnableProperty, value); }
		}

		public static readonly DependencyProperty IsWriteEnableProperty =
			DependencyProperty.Register("IsWriteEnable", typeof(bool), typeof(UInkCanvas), new UIPropertyMetadata(true));

        public object Tag;

        #endregion

		void handler_MultiTouch(MultiTouchEventArgs args)
		{
			foreach (FingerEventArgs e in args.FingerEvents)
			{
				switch (e.EventType)
				{
					case FingerEventType.FINGER_DOWN_EVENT:
						UDrawingPanel_OnFingerDown(e);
						break;
					case FingerEventType.FINGER_MOVE_EVENT:
						UDrawingPanel_OnFingerMove(e);
						break;
					case FingerEventType.FINGER_UP_EVENT:
						UDrawingPanel_OnFingerUp(e);
						break;
				}
			}
		}

		void UDrawingPanel_OnFingerDown(FingerEventArgs args)
		{
			// Console.WriteLine("Drawing Panel1: " + args.FingerID + " : " + args.EventType + " : (" + args.Position.X + "," + args.Position.Y + ")");
            Polyline line = new Polyline();
			line.Stroke = new SolidColorBrush(WritingStrokeColor);
			line.StrokeThickness = WritingStrokeThickness;
			line.Width = this.ActualWidth;
			line.Height = this.ActualHeight;
			Canvas.SetLeft(line, 0);
			Canvas.SetTop(line, 0);
			this.WritingCanvas.Children.Add(line);
			line.Points.Add(args.Position);
			LineMap.Add(args.FingerID, line);


			System.Windows.Input.StylusPointCollection stroke = new System.Windows.Input.StylusPointCollection();
			stroke.Add(new System.Windows.Input.StylusPoint(args.Position.X, args.Position.Y));
			StrokeMap.Add(args.FingerID, stroke);

            if (OnEventOccurred != null && !OnlyNotifyLineEnd)
            {
                this.OnEventOccurred(this, new UInkCanvasEventArgs(UInkCanvasEventArgs.EventType.LineStart, line, args.FingerID));
            }
		}

        void UDrawingPanel_OnFingerMove(FingerEventArgs args)
		{
			if (LineMap.ContainsKey(args.FingerID))
			{
                Polyline line = LineMap[args.FingerID];
				line.Points.Add(args.Position);

				StrokeMap[args.FingerID].Add(new System.Windows.Input.StylusPoint(args.Position.X, args.Position.Y));

                if (OnEventOccurred != null && !OnlyNotifyLineEnd)
                {
                    this.OnEventOccurred(this, new UInkCanvasEventArgs(UInkCanvasEventArgs.EventType.LineUpdate, line, args.FingerID));
                }
			}

		}

        void UDrawingPanel_OnFingerUp(FingerEventArgs args)
		{
			// Console.WriteLine("Drawing Panel3: " + args.FingerID + " : " + args.EventType + " : (" + args.Position.X + "," + args.Position.Y + ")");
			if (LineMap.ContainsKey(args.FingerID))
			{
                if (OnEventOccurred != null)
                {
                    this.OnEventOccurred(this, new UInkCanvasEventArgs(UInkCanvasEventArgs.EventType.LineEnd, LineMap[args.FingerID], args.FingerID));
                }

				this.WritingCanvas.Children.Remove(LineMap[args.FingerID]);
				LineMap.Remove(args.FingerID);
				Stroke stroke = new Stroke(StrokeMap[args.FingerID]);
				stroke.DrawingAttributes.FitToCurve = true;
				stroke.DrawingAttributes.IsHighlighter = true;
				stroke.DrawingAttributes.Color = WrittenStrokeColor;
				stroke.DrawingAttributes.Width = WrittenStrokeThickness;
				this.WritingCanvas.Strokes.Add(stroke);
				StrokeMap.Remove(args.FingerID);
			}
		}

		Dictionary<int, Polyline> LineMap = new Dictionary<int, Polyline>();
		Dictionary<int, System.Windows.Input.StylusPointCollection> StrokeMap = new Dictionary<int, System.Windows.Input.StylusPointCollection>();

        public event UInkCanvasEventHandler OnEventOccurred;
	}

    public class UInkCanvasEventArgs
    {
        public UInkCanvasEventArgs(EventType type, Stroke stroke, int id)
        {
            this.Type = type;
            this.Stroke = stroke;
            this.LineID = id;
        }

        public UInkCanvasEventArgs(EventType type, Polyline line, int id)
        {
            this.Type = type;
            this.Line = line;
            this.LineID = id;
        }

        public enum EventType
        {
            LineStart,
            LineUpdate,
            LineEnd,
        }

        public EventType Type { get; protected set; }

        public Polyline Line { get; protected set; }

        public Stroke Stroke { get; protected set; }

        public int LineID { get; protected set; }
    }

    public delegate void UInkCanvasEventHandler(UInkCanvas sender, UInkCanvasEventArgs args);
}